/*****************************************************************************

                 ,//////,   ,////    ,///' /////,
                ///' ./// ///'///  ///,    ,, //
               ///////,  ///,///   '/// //;''//,
             ,///' '///,'/////',/////'  /////'/;,

    Copyright 2010 Marcus Jansson <mjansson256@yahoo.se>

    This file is part of ROSA - Realtime Operating System for AVR32.

    ROSA is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    ROSA is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with ROSA.  If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
/* Tab size: 4 */
#include <stdint.h>
#include <stdlib.h>
#include "kernel/rosa_systick.h"
#include "kernel/rosa_ext.h"
#include "kernel/rosa_scheduler.h"
#include "kernel/rosa_queues.h"
#include "kernel/rosa_util.h"
#include "kernel/rosa_int.h"

/// EXECTASK: Global variable that contain the current running TCB.
tcb * EXECTASK, *OLDTASK;

/// Keeps track of the original task/stack frame that called startScheduler
tcb originalTask;

enum {Stopped, Started} schedulerState = Stopped;

static bool stackPointerValid(tcb *c)
{
	return (c->saveusp >= c->dataarea) &&
	(c->saveusp < (c->dataarea + c->datasize));
}

void scheduler(void)
{
	if(schedulerState != Started)
		return;
		
	// Stop systick to protect internal data structures
	stopSysTick();
	
	switch(EXECTASK->state)
	{
		case Ready:
		case Running:
			if ( readyQueueHead->dynamicPriority > EXECTASK->dynamicPriority)
			{
				rosa_addToReadyQueueFront(EXECTASK);
				EXECTASK->state = Ready;
			}
			else
			{
				startSysTick();
				return;
			}
			break;
		case Waiting:
			rosa_addToWaitingQueue(EXECTASK);
			break;
		case Blocked:
			if(EXECTASK->semStatus == SOS_FiniteWait)
				rosa_addToWaitingQueue(EXECTASK);		// Finite wait - add to waiting queue
			else if(EXECTASK->semStatus == SOS_InfiniteWait)
				rosa_addToBlocked(EXECTASK);			// Blocked
			else
				assert(false);	// Error: unexpected semaphore operation status
			break;
		case Terminated:
			rosa_addToDelete(EXECTASK);
			break;
	}
	
	// Save currently executing task's state
	OLDTASK = EXECTASK;
	EXECTASK = rosa_popHighestPriorityTaskFromReadyQueue();
	EXECTASK->state = Running;
	if(!stackPointerValid(EXECTASK))
	{
		asm("breakpoint");
	}
	
	ROSA_yield();
}

/// Starts the scheduler
void ROSA_startScheduler(void)
{
	OLDTASK = &originalTask;
	schedulerState = Started;
	
	ROSA_saveOriginalContext();
	if(schedulerState == Started)	// If we want to start scheduler
	{
		EXECTASK = rosa_popHighestPriorityTaskFromReadyQueue();
		ROSA_start();	// Does not return
	}
}

void ROSA_freeQueue(tcb *queueHead)
{
	tcb *next, *iterator = queueHead;
	while(iterator)
	{
		next = iterator->nexttcb;
		freeTask(iterator);
		iterator = next;
	}	
}

void ROSA_freeAllResources(void)
{
	SemaphoreImpl *next, *iterator = semaphoreListHead;
	while(iterator)
	{
		next = iterator->next;
		free(iterator);
		iterator = next;
	}
	semaphoreListHead = NULL;
	
	ROSA_freeQueue(readyQueueHead);
	readyQueueHead = NULL;
	ROSA_freeQueue(waitingQueueHead);
	waitingQueueHead = NULL;
	ROSA_freeQueue(blockedQueueHead);
	blockedQueueHead = NULL;
	ROSA_freeQueue(deleteQueue);
	deleteQueue = NULL;
}

void ROSA_stopScheduler(void)
{
	bool systickEnabled = systickIsRunning();
	stopSysTick();
	assert(systickEnabled);
	OLDTASK = EXECTASK;
	rosa_addToReadyQueue(EXECTASK);
	EXECTASK = &originalTask;
	schedulerState = Stopped;
	ROSA_restoreOriginalContext();
}

/// Returns the currently executing task
tcb *getCurrentTask(void)
{
	return EXECTASK;
}

int getMaximumStackUsage(tcb *task)
{
	uint8_t *c;
	for(c = task->dataarea; c != task->dataarea + task->datasize; c++)
	{
		if(*c != 0xCC)
			break;
	}
	return (intptr_t)task->dataarea + task->datasize - (intptr_t)c;
}